#!/usr/bin/env perl

use strict;
use warnings;
use CGI qw(:standard );
use POSIX;
use Data::Dumper;
use Time::Local;

our %in;
CGI::ReadParse();

my $SITE_NAME = "www.myfishinghome.com";
my $DATA_DIR = '.';
my $TEMPLATE_FILE = 'template.html';
my $METADATA_FILE = 'metadata.txt';
my $CONTENT_FILE = 'content.html';
my $THUMB_TEMPLATE = 'thumb_template200x150.png';
my $ERROR_FILE = "$DATA_DIR/errors.txt";

# Return a content of the given file
sub read_file($) {
  open(FD, $_[0]) or die "Cannot open $_[0]: $!\n";
  local $/ = undef;
  binmode FD;
  my $text = <FD>;
  close FD;
  return $text;
};

# throws an errors. use for debugging
sub error {
	my ($message) = @_;
	open F, ">>$ERROR_FILE";
	print F $message;
	close F;
	return 1;
}

# make a list of all directories
sub generate_dir_list {
  my @dirs = ();
  open (my $fh, "-|", "find", "$DATA_DIR", "-type", "d" );
  while (<$fh>) {
    my $dir = $_;
    chomp($dir);
    push @dirs, $dir if -e "$dir/$METADATA_FILE";
  }
  close($fh);
  return @dirs;
}

sub generate_tags {
  my ($dirs) = @_;
  my @tags = ();
  foreach my $dir (@$dirs) {
    my $metadata = eval(read_file("$dir/$METADATA_FILE"));
    my $tag_str = $metadata->{tags};
	  next unless $tag_str;
    push @tags, split(',', $tag_str);
  }

  my $max_weight = 1;
  # build a hash of the tags to calculate a weights
  my $tag_hash = {};
  foreach my $tag (@tags) {
	  $tag =~ s/^\s+//gis;  
	  $tag =~ s/\s+$//gis;  
	  $tag =~ s/\s+/ /gis;  
    if (exists $tag_hash->{$tag}) {
      $tag_hash->{$tag}++;
      if ($tag_hash->{$tag} > $max_weight) {
        $max_weight = $tag_hash->{$tag};
      }
    } else {
      $tag_hash->{$tag} = 1;
    }
  }

  my $tag_str = '';
  my $max_size = 6; # font size of the haviest tag
  # now, add a tags
  foreach my $key (sort {lc($a) cmp lc($b)} keys %$tag_hash) {
    my $size = 100 + 10 * floor($tag_hash->{$key} * $max_size / $max_weight);
    my $style = "font-size:$size%";
		my $var = $key;
		$var =~ s/\s+/%20/g;
    $tag_str .= a({-href=>"/tags/?k=$var", -style=>"$style;"}, $key) . " ";
  }
  return $tag_str;
}


# create a breadcrumbs
sub generate_breadcrumbs {
  my ($path) = @_;
  return '' unless $path;

  my @breadcrumbs = ();

  my @folders = split('/', $path);
  my $full_path = "";
  foreach my $folder (@folders) {
    $full_path .= $full_path ? "/" . $folder : ".";
    my $metadata = eval(read_file("$full_path/metadata.txt"));
    if ($@) { error("$full_path/metadata.txt\n".$@); }
		my $title = $metadata->{title} || 'Нет заголовка';
    my $uri = $full_path;
    my $crumb = '';
    if ($uri eq $path) {
      # this is a last folder. mark it as active
      $crumb = li({-class=>'active'}, $title);
    } else {
      $uri =~ s/\.//gis;
			$uri .= "/";
      # this is ordinary breadcrumb, show it as a link
      $crumb = li(a({-href=>"$uri"}, $title) .
        span({-class=>'divider'}, '&nbsp;/&nbsp;')
      );
    }

    push @breadcrumbs, $crumb;
  }

  my $list = '';
  $list = ul({-class=>'breadcrumb'}, @breadcrumbs) if scalar @breadcrumbs > 1;
  return $list;

}


# remove html tags form the string
# cut and return $chars_num first characters of the string
# if $elegant then return the substring logically terminated on dot "."
sub strip_string
{
  my ($str, $chars_num, $elegant) = @_;
  return $str if length($str) <= $chars_num;
  my $stripped_string = '';
  if ($elegant) {
    my @arr = split('\.', $str);
    foreach my $sub (@arr) {
      last if length($stripped_string) >= $chars_num;
      $stripped_string .= "$sub. ";
    }
  } else {
    $stripped_string = substr($str, 0, $chars_num);
  }
  return $stripped_string;
}

# generate a content of the columns on a home page
sub generate_home_page_column
{
  my ($pages_array) = @_;
  my $column = '';
  foreach my $page_hash (@$pages_array) {
    my $dir = $page_hash->{dir};
    my $metadata = $page_hash->{metadata};
    my $title = $metadata->{title};
    my $content = read_file("$dir/$CONTENT_FILE");
   	$content =~ s/<.*?>//gis; 
    my $img = '';
    if ($metadata->{home_thumb} and -f "$dir/$metadata->{home_thumb}") {
			my $src = "$dir/$metadata->{home_thumb}";
			$src =~ s/^\.//g;
      $img = img({-src=>"$src", -class=>'image', -alt=>"$title"});
    }
		my $href = $dir;
		$href =~ s/^\.//g;
    $column .= "<div>" .
      a({-href=>"$href/"}, $title) . br() .
      $img .
      strip_string($content, 400, 1) .
      p({-class=>"clear"}, "&nbsp;") . 
      "</div>";
  }
  return $column;
}

# return now() if badly constructed
sub get_creationtime {
	my ($creationtime) = @_;
	$creationtime = '01.01.1970 00:00' unless $creationtime;
	my ($mday, $mon, $year, $hour, $min) = 
		$creationtime =~ /^(\d+)\.(\d+)\.(\d+)\s+(\d+):(\d+)$/gis;
	my $time = time();
	if ($mday and $min) {
		# suppose the creation time is parsed correctly
		$time = timelocal(00,$min,$hour,$mday,$mon,$year);
	}
	return $time;
}

# creating a content of the home page
# it should contain the 10 newest recipes and articles
sub generate_home_page {
  my ($dirs) = @_;

  my $hash = {
    article => {},
    recipe => {}
  };
  foreach my $dir (@$dirs) {
    my $metadata = eval(read_file("$dir/$METADATA_FILE"));
    next unless exists $hash->{$metadata->{type}};
    my $time = get_creationtime($metadata->{creationtime});
    # fill the hash. use timestamp as a key and path to a content as a value
    $hash->{$metadata->{type}}->{$time} = {dir=>$dir, metadata=>$metadata};
  }

  my $latest_pages = {};
  my $latest_pages_count = 5;
  foreach my $type (keys %$hash) {
    foreach my $time_key (reverse sort {$a <=> $b} keys %{$hash->{$type}}) {
			if (exists $hash->{$type}->{$time_key}->{metadata}->{home_hide} and
				 	$hash->{$type}->{$time_key}->{metadata}->{home_hide} == 1) {
				next;
			}
			last if exists $latest_pages->{$type} and 
				scalar(@{$latest_pages->{$type}}) >= $latest_pages_count;
      push @{$latest_pages->{$type}}, $hash->{$type}->{$time_key};
    }
  }

  my $articles_column = generate_home_page_column($latest_pages->{article});
  my $recipes_column = generate_home_page_column($latest_pages->{recipe});

  # dont use CGI call div(). It has an impact on a performance
  my $data = "<div class='span6'><h2>Новые статьи</h2>$articles_column</div>";
  $data .= "<div class='span6'><h2>Новые рецепты</h2>$recipes_column</div>";
  return $data;
}

# split comma-separated string to arrray
sub str_to_arr {
  my ($str) = @_;
  my @arr = split(',', $str);
  foreach my $item (@arr) {
    $item =~ s/^\s+//gis;
    $item =~ s/\s+$//gis;
    $item =~ s/\s+/ /gis;
  }
  return @arr;
}

sub generate_tags_page {
  my ($dirs) = @_;
  my $cgi_tag = $in{k};
  
  my @arr = ();
  my $page = "";
  foreach my $dir (@$dirs) {
    my $metadata = eval(read_file("$dir/$METADATA_FILE"));
    my $tag_str = $metadata->{tags};
    next unless $tag_str;
    my @tags = str_to_arr($tag_str);
    my $found_tag = 0;
    foreach my $tag (@tags) {
      if ($tag eq $cgi_tag) {
        $found_tag = 1;
        last;
      }
    }
    if ($found_tag) {
        push @arr, {
          dir => $dir,
          title => $metadata->{title},
          creationtime => $metadata->{creationtime},
        };  
    }
  }
  
  foreach my $hash (reverse sort {get_creationtime($a->{creationtime}) <=> get_creationtime($b->{creationtime})} @arr) {
    my $content = read_file("$hash->{dir}/$CONTENT_FILE");
    $content =~ s/<.*?>//gis;
    $page .= p(a({-href=>"/$hash->{dir}/"}, $hash->{title}));
    $page .= strip_string($content, 600, 1);
    $page .= hr();
  }

  return $page; 
}

# generate a bunch of related links based on a tags and a page type
sub generate_related_pages_links {
  my ($metadata, $current_dir, $dirs) = @_;
  return '' unless $metadata->{tags};
  my $count = 5; # number of related links
  # create a hash with tags as a keys
  my $tags_hash ={};
  my @tags = str_to_arr($metadata->{tags});
  map { $tags_hash->{$_} = 1; } @tags;

  my @links_arr = ();
  foreach my $dir (@$dirs) {
		next if $dir eq $current_dir;
    my $meta = eval(read_file("$dir/$METADATA_FILE"));
    # do not display a links to the pages of different types
    next unless $meta->{type} eq $metadata->{type};
    my $tag_str = $meta->{tags};
    next unless $tag_str;
    my @arr = str_to_arr($meta->{tags});
    my $found = 0;
    foreach my $elem (@arr) {
      if (exists $tags_hash->{$elem}) {
        $found = 1; 
        last;
      }
    }
    
    if ($found) {
      push @links_arr, {'dir' => "/$dir", 'title' => $meta->{title}};
      last if scalar @links_arr >= $count;
    }
  }
  
  return '' unless scalar @links_arr > 0;
  
  my @links;
  map {
    my $h = $_; 
    push @links, li(a({-href=>"$h->{dir}/"}, $h->{title}));      
  } @links_arr;
 
  my $related_links = p({-class=>'clear'});
	$related_links .=  "<div class='well'>" . h3("Материалы по теме") .  ul(@links) . "</div>";
  return $related_links;
}

sub generate_tags_links {
	my ($metadata) = @_;
	my @tags = str_to_arr($metadata->{tags});
	return '' unless scalar @tags > 0;
	my $str = p({-class=>'clear'}) . hr() . b("Теги: ");
	foreach my $tag (@tags) {
		my $var = $tag;
		$var =~ s/\s+/%20/g;
		$str .= a({-href=>"/tags/?k=$var"}, "$tag ");
	}
	return $str;
}

MAIN: {
  my ($dir, $file) = $ENV{'REQUEST_URI'} =~ /(.*)\/(.*)/;
  my $request_dir = "$DATA_DIR$dir";
  my $location = "/";
  my $redirect = 0;
  if ($dir and not -d $request_dir) {
    $redirect = 1;
    $location = "/";
  }
  if ($redirect) {
    print "Status: 302 Moved\r\nLocation: $location\r\n\r\n"
  }

  my $page = read_file("$DATA_DIR/$TEMPLATE_FILE");
  my $metadata = eval(read_file("$request_dir/$METADATA_FILE"));

  # make the tags cloud
  my @dirs = generate_dir_list();
  my $tags = generate_tags(\@dirs);
  my $breadcrumbs = generate_breadcrumbs($request_dir);
  my $content = '';
  my $metatitle = $metadata->{metatitle} ||$metadata->{title};
  my $title = $metadata->{title_hide} ? '' : h1($metadata->{title}) . hr();

  if ($metadata->{type} eq 'home') {
    $content = generate_home_page(\@dirs);
    $metatitle = "$SITE_NAME - $metatitle";
  } elsif ($metadata->{type} eq 'tags') {
    $content = generate_tags_page(\@dirs);
  } else {
    $content = read_file("$request_dir/$CONTENT_FILE");
		$content .= generate_tags_links($metadata);
    $content .= generate_related_pages_links($metadata, $request_dir, \@dirs);
    $metatitle = "$metatitle - $SITE_NAME";
  }

  my $canonical = $metadata->{canonical} || '';
  my $keywords = $metadata->{keywords} || '';
  my $description = $metadata->{description} || '';
  $page =~ s/__METATITLE__/$metatitle/gis;
  $page =~ s/__TITLE__/$title/gis;
  $page =~ s/__KEYWORDS__/$keywords/gis;
  $page =~ s/__DESCRIPTION__/$description/gis;
  $page =~ s/__CANONICAL__/$canonical/gis;
  $page =~ s/__CONTENT__/$content/gis;
  $page =~ s/__TAGS__/$tags/gis;
  $page =~ s/__BREADCRUMBS__/$breadcrumbs/gis;

  print "Content-Type: text/html\r\n\r\n", $page;
}
